﻿using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web.Compilation;
using System.Web.Mvc;
using System.Web.Routing;
using System.Xml;

namespace Clover.Auth
{
    public class AuthControllerFactory : DefaultControllerFactory
    {
        public const string CACHE = "RoleCache.json";
        internal TypeCacheSerializer serializer = new TypeCacheSerializer();
        public AuthControllerFactory()
        {

        }


        protected override Type GetControllerType(RequestContext requestContext, string controllerName)
        {
            var controllerType = base.GetControllerType(requestContext, controllerName);

            /*CustomType*/

           var types= ResolveType(IsControllerType);
            var model = ResolveRole(types);

            WriteCacheFile(model);
            return controllerType;
        }

        public RoleModule ResolveRole(IEnumerable<Type> types) {
            RoleModule roleModule = new RoleModule
            {
                Controllers = new ControllerDescriptorCollection()
            };

            foreach (var type in types)
            {
                var controllerDescriptor = new ControllerDescriptor
                {
                    ActionDescriptors = new ActionDescriptorCollection(),
                    ControllerName = type.Name
                };

                foreach (var actionDescriptor in (type as TypeInfo).DeclaredMethods.Where(m => m.Name.EndsWith("View")).Select(m => new ActionDescriptor() { Name = m.Name }))
                {
                    controllerDescriptor.ActionDescriptors.Add(actionDescriptor);
                }

                roleModule.Controllers.Add(controllerDescriptor);
            }

            return roleModule;
        }

        public RoleModule ReadCacheFile()
        {
            using (var stream = BuildManager.ReadCachedFile(CACHE))
            {
                using (var read = new StreamReader(stream)) {

                   return JsonConvert.DeserializeObject<RoleModule>(read.ReadToEnd());
                }

            }
        }

        public void WriteCacheFile(RoleModule module)
        { 
            using (var stream = BuildManager.CreateCachedFile(CACHE))
            {
                using (var sr = new StreamWriter(stream))
                {
                    sr.Write(JsonConvert.SerializeObject(module));
                }
            }
        }

        public IEnumerable<Type> ResolveType(Predicate<Type> predicate) {
            IEnumerable<Type> typesSoFar = Type.EmptyTypes;

            ICollection assemblies = BuildManager.GetReferencedAssemblies();
            foreach (Assembly assembly in assemblies)
            {
                Type[] typesInAsm;
                try
                {
                    typesInAsm = assembly.GetTypes();
                }
                catch (ReflectionTypeLoadException ex)
                {
                    typesInAsm = ex.Types;
                }
                typesSoFar = typesSoFar.Concat(typesInAsm);
            }
            return typesSoFar.Where(type => TypeIsPublicClass(type) && predicate(type));
        }

        private static bool TypeIsPublicClass(Type type)
        {
            return (type != null && type.IsPublic && type.IsClass && !type.IsAbstract);
        }

        internal static bool IsControllerType(Type t)
        {
            return
                t != null &&
                t.IsPublic &&
                t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                !t.IsAbstract &&
                typeof(IController).IsAssignableFrom(t);
        }
    }


    
}
